#!/usr/bin/env python2.5
# -*- coding: UTF-8 -*- 
import datetime
import base64
 
from google.appengine.ext import webapp
#from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.api import memcache
from google.appengine.ext import db
import logging  
#from nmpacks.milonpy import NMhtml as html 
import NMhtml as html
logging.getLogger().setLevel(logging.DEBUG) 
import apprvars_readonly #@UnresolvedImport OK apprvars_readonly should reside in the package which imports this module
StaticRequestEtag=apprvars_readonly.VarDic['Version'] 
sys = None

class NM_DScashe(db.Model):
    """
    DataStore Cashe
    """ 
    ##'text/plain' text/html
    ##ck = db.StringProperty()  # cache key
    ##namespace=db.StringProperty() 
    ct = db.DateTimeProperty(auto_now_add=True)  #content time
    to = db.DateTimeProperty() #time out
    etag=db.StringProperty(indexed=False) 
    co_txt =  db.TextProperty() 
    co_blob = db.BlobProperty()  
    def set(self,  ck, vl, to=None, **kwargs): 
        if isinstance(vl, unicode):   
            newItem = NM_DScashe(key_name=ck, ck=ck, co_txt=vl, etag=str(hash(vl)),  **kwargs)  
        else:    
            newItem = NM_DScashe(key_name=ck, ck=ck, co_blob=vl, etag=str(hash(vl)),  **kwargs) 
        return newItem.put()  
#TODO : delete it if we do not use it 
#    @classmethod
#    def setNICK(path, body, content_type, **kwargs):
#        content = StaticContent(
#        key_name=path,
#        body=body,
#        content_type=content_type,
#        **kwargs)
#        content.put()
#        return content  
    @classmethod        
    def getBetween (cls, k1, k2, howMany=1000):
        f1=db.Key.from_path (cls.kind(), k1)
        f2=db.Key.from_path (cls.kind(), k2)
        q=cls.all().filter("__key__ >=", f1).filter("__key__ <", f2) 
        return q.fetch(howMany )
    @classmethod       
    def get (self, ck):
        #rpc = db.create_rpc(read_policy=db.EVENTUAL_CONSISTENCY)
        return self.get_by_key_name(ck, rpc= db.create_rpc(read_policy=db.EVENTUAL_CONSISTENCY)) #, rpc=self.getRpc)   
    def getContent(self, item): 
        if not item.co_txt is None:return item.co_txt
        elif not item.co_blob is None: return item.co_blob
        else:return None
#    @classmethod
#    def delete(self, ck): 
#        #q = db.GqlQuery("SELECT __key__ FROM NM_DScashe) # WHERE create_date < :1", earliest_date)
#        q=self.all()
#        q.keys_only=True
#        results = q.fetch(1)
#        db.delete(results) 
    @classmethod
    def flush_byKey(cls, ck):
        item=cls.get_by_key_name(ck)
        if not item is None: db.delete(item)   
    @classmethod
    def flush_all(cls, batchsize=999):
        q=cls.all()
        q.keys_only=True 
        r=q.fetch(batchsize)
        if len(r) == 0 : return True
        else:
            db.delete(r)
            cls.flush_all(batchsize)
    @classmethod
    def flush_allOLD(self):
        q=self.all()
        q.keys_only=True 
        for item in q:
            db.delete(item)  
            
class ReqHadlerNM(webapp.RequestHandler):
    tmplD=apprvars_readonly.VarDic
    useCashe = 3    # 0 = nocashe, 1=Dictionary Cashe, 2=Memcache,4=big Table cashe, 7=all
    dbCashe = NM_DScashe()
    isStaticContent=True
    content_language="en" 
    cookieName_Path=[]
    cookieReceived = None
    cookieToSend = None
    exeption = None  
    def isMemCashe(self):return (self.useCashe  & 2)==2 
    def isDBCashe (self):return (self.useCashe  & 4)==4  
    def cacheBuster(self, uri):
        return "%s?&version=%s" %(uri, self.tmplD['Version'])
    def cacheKey(self): 
        return  self.request.path_info 
        # can be overiten in subclasses i.e: return "%s?%s" %(self.request.path_info, self.request.query_string) 
    def Responce_ContentType(self) : 
        #return None  # = let App Engine handle it
        return "text/html; charset=utf-8"
    def serveOr304 (self):
        serve = True
#        if 'If-Modified-Since' in self.request.headers:
#            last_seen = datetime.datetime.strptime(self.request.headers['If-Modified-Since'],HTTP_DATE_FMT)
#            if last_seen >= content.last_modified.replace(microsecond=0):
#               serve = False
        if 'If-None-Match' in self.request.headers:
            etags = [x.strip('" ')  for x in self.request.headers['If-None-Match'].split(',')] 
            if StaticRequestEtag in etags:serve = False 
        return serve
    
    def raiseErrCode(self, code=400, msg=''):
        self.response.set_status(code)
        tmp= "%s %6d %s" %('Error', code,  msg)
        self.response.out.write (html.h1( tmp ))
        return None   
    def raiseErrCode400(self,msg=''):return self.raiseErrCode(400,'- Bad Request No such Page:'+ msg)
    def unescape(self, vl):
        if isinstance (vl,  (list, tuple)): return [self.unescape(x) for x in vl]
        else:return vl.replace('%20', ' ')  
    def handle_exception(self, exception, debug_mode):
        self.exeption = exception
        logging.info('Exc'+ str(self.exeption) ) 
        if debug_mode:
            super(ReqHadlerNM, self).handle_exception(exception, debug_mode)
        else:
            super(ReqHadlerNM, self).handle_exception(exception, debug_mode) 
#        else:
#        if isinstance(exception, datastore_errors.Timeout):
#            # Display a timeout-specific error page
#        else:
#        # Display a generic 500 error page.
    def post(self,  *args):
        self.response.out.write (self.postC(*args)  )
    def head(self, *args): 
        return self.get(*args)  
    def get(self,*args):
        args=self.unescape(args)
        ##print str(self.cookieName_Path)
        if self.cookieName_Path !=[] : 
            if self.cookieName_Path[0] in self.request.cookies:  
                self.cookie = html.attrs (str(base64.b64decode(self.request.cookies[self.cookieName_Path[0]])))  ## +"=" to avoid 'Incorrect padding' errors
                logging.info("cookie:" +  self.cookie())  
                self.cookie['NumOfVisits'] = str(int(self.cookie['NumOfVisits'])+1)  
            else:
                self.cookie = html.attrs()  
                self.cookie['dummy']='XX'         #coze there is a bug in first  
                self.cookie['FirstVisitDT']  = datetime.datetime.now() 
                self.cookie['NumOfVisits']='1'  
            self.cookie['LastVisitURL'] = self.request.path_info 
            self.cookie['LastVisitDT']  = datetime.datetime.now()  
            self.response.headers.add_header('Set-Cookie', self.cookieName_Path[0] +'=' + str(base64.b64encode(self.cookie.contents)), path= self.cookieName_Path[1], expires='Fri, 31-Dec-2010 23:59:59 GMT')
    
        output=None
        
        self.useCashe =min(self.useCashe, 4)     # temporary
        if self.isStaticContent:
            self.response.headers['ETag'] = '"%s"' % (StaticRequestEtag,)
            serve= self.serveOr304()
            if not serve:
                self.response.set_status(304)
                return 
        def tryGetC(): 
            try :   #! this try so we not cash Pages with Errors  so it will not pollute cashe if something goes wrong    
                return self.getC(*args)   
            except:
                global sys
                if  sys is None: import sys
                logging.info("NMServer Error ! :" + str(sys.exc_info()) )
                return False
                
        def tryGetFromDBCashe(updateMemCache=False):
            res=self.dbCashe.get(cacheKey)  
            #print 'res ' * 10,  str(res)
            if res is None or not self.exeption is None: 
                logging.info("%s:%s" %("DBCache Fail:", cacheKey) ) 
                res=tryGetC() 
                #print str(res), 'sss ' * 10
                if res:
                    if self.exeption is None:self.dbCashe.set(cacheKey, res, self.Responce_ContentType())  
                    #elif isinstance(exception, datastore_errors.Timeout):pass TO DO : handle the error
                    if updateMemCache: memcache.add(cacheKey,  res, 604800) 
            else:  
                    #logging.info("DBCache Hit:" + cacheKey)  
                    return self.dbCashe.getContent(res)
            return res  
        if self.useCashe ==0  or apprvars_readonly.VarDic['nocashe'] or self.request.get('nocashe') !='': output = self.getC(*args)  
        else:
            cacheKey = self.cacheKey() 
            if self.isMemCashe():
                output = memcache.get(cacheKey) 
                if output is None:
                    logging.info("MemCache Fail:" + cacheKey)
                    if self.isDBCashe(): 
                        output=tryGetFromDBCashe(True)    
                    else:
                        output=tryGetC() 
                        logging.info('tryGetC MEM') 
                        if output:memcache.set(cacheKey,  output, 604800)   
                else: 
                    logging.info("MemCache Hit:" + cacheKey )      
            elif self.isDBCashe():  
                output=tryGetFromDBCashe() 
        
        self.response.headers['Content-Language'] =self.content_language 
        if not self.Responce_ContentType() is None : self.response.headers['Content-Type'] = self.Responce_ContentType()
        self.response.out.write (output)
